package cz.apnetis.fitweb.server;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import com.google.appengine.api.datastore.Key;
import com.google.gwt.user.client.rpc.RemoteService;

import cz.apnetis.fitweb.client.FoodService;
import cz.apnetis.fitweb.server.entity.FoodEntity;
import cz.apnetis.fitweb.shared.Constants;
import cz.apnetis.fitweb.shared.Food;
import cz.apnetis.fitweb.shared.YearPeriod;

public class FoodServiceImpl implements RemoteService, FoodService {

  private GenericDAO<FoodEntity, Key> foodDao; public final GenericDAO<FoodEntity, Key> getFoodDao() { return this.foodDao; } public final void setFoodDao(final GenericDAO<FoodEntity, Key> newFoodDao) { this.foodDao = newFoodDao; }

  /** User which work with their food */
  private User user; public User getUser() { return this.user; } public void setUser(final User user) { this.user = user; }

  @Override
  public final String save(final Food food) throws IllegalArgumentException {
    if (getUser() == UserFactory.GUEST) { throw new IllegalStateException("User must be logged. This operation isn't permited for Guest."); }

    if (food == null) {
      throw new IllegalArgumentException("Food mustn't be null");
    }

    final FoodEntity entity = FoodEntity.instanceFromDTO(food);

    if (food.getId() != null && !"".equals(food.getId())) {
      final Food uf = getFoodDao().get(entity.getKey());
      if (!this.user.getUserId().equals(uf.getUserId())) {
        throw new SecurityException("Owner of food is different from current user. "
            + this.user.getUserId() + " and owner of food is: " + uf.getUserId());
      }
      entity.setUserId(uf.getUserId());
    }

    entity.setUserId(getUser().getUserId());
    getFoodDao().save(entity);

    return entity.getId();
  }

  @Override
  public final String remove(final Food food) throws IllegalArgumentException {
    if (getUser() == UserFactory.GUEST) { throw new IllegalStateException("User must be logged. This operation isn't permited for Guest."); }

    if (food == null || food.getId() == null || "".equals(food.getId())) {
      throw new IllegalArgumentException("Identifier of removed food must be defined");
    }

    final FoodEntity entity = FoodEntity.instanceFromDTO(food);

    if (food.getId() != null && !"".equals(food.getId())) {
      final Food uf = getFoodDao().get(entity.getKey());
      if (!this.user.getUserId().equals(uf.getUserId())) {
        throw new SecurityException("Owner of food is different from current user.");
      }
    }

    getFoodDao().remove(entity.getKey());
    return null;
  }

  @Override
  public final List<Food> allFood() {
    if (getUser() == UserFactory.GUEST) { throw new IllegalStateException("User must be logged. This operation isn't permited for Guest."); }

    return listOfEntityToDTO(getFoodDao().executeQuery("s.userId", getUser().getUserId()));
  }

  private final List<Food> listOfEntityToDTO(final List<FoodEntity> list) {
    final List<Food> result = new ArrayList<Food>(list.size());
    for (final FoodEntity fe : list) {
      result.add(fe.toDto());
    }
    return result;
  }

  @Override
  public final List<Food> foodOfPeriod(final int period, final int offset, final int timeZoneOffset) {
    TimeZone.getAvailableIDs(timeZoneOffset);
    final TimeZone tz;
    tz = TimeZone.getTimeZone("GMT");

    final Calendar timeFrom = Calendar.getInstance();
    timeFrom.setTimeZone(tz);
    timeFrom.set(Calendar.HOUR_OF_DAY, 0);
    timeFrom.set(Calendar.MINUTE, 0);
    timeFrom.set(Calendar.SECOND, 0);
    timeFrom.set(Calendar.MILLISECOND, 0);

    final Calendar timeTo = Calendar.getInstance();
    timeTo.setTimeZone(tz);
    timeTo.set(Calendar.HOUR_OF_DAY, 0);
    timeTo.set(Calendar.MINUTE, 0);
    timeTo.set(Calendar.SECOND, 0);
    timeTo.set(Calendar.MILLISECOND, 0);
    timeTo.add(Calendar.DAY_OF_YEAR, 1);

    final YearPeriod per = YearPeriod.values()[period];
    switch (per) {
      case DAY :
        if (offset > 0) {
          timeFrom.add(Calendar.DAY_OF_YEAR, -1 * offset);
          timeTo.add(Calendar.DAY_OF_YEAR, -1 * offset);
        }
        break;
      case TWO_DAY :
        timeFrom.add(Calendar.DAY_OF_YEAR, -1);
        if (offset > 0) {
          timeFrom.add(Calendar.DAY_OF_YEAR, -2 * offset);
          timeTo.add(Calendar.DAY_OF_YEAR, -2 * offset);
        }
        break;
      case WEEK :
        timeFrom.set(Calendar.DAY_OF_WEEK, 1);
        timeTo.set(Calendar.DAY_OF_WEEK, 7);
        if (offset > 0) {
          timeFrom.add(Calendar.DAY_OF_YEAR, -7 * offset);
          timeTo.add(Calendar.DAY_OF_YEAR, -7 * offset);
        }
        break;
      case MONTH :
        timeFrom.set(Calendar.DAY_OF_MONTH, 1);
        timeTo.set(Calendar.DAY_OF_MONTH, timeTo.getActualMaximum(Calendar.DAY_OF_MONTH));
        if (offset > 0) {
          timeFrom.add(Calendar.MONTH, -1 * offset);
          timeTo.add(Calendar.MONTH, -1 * offset);
          timeTo.set(Calendar.DAY_OF_MONTH, timeTo.getActualMaximum(Calendar.DAY_OF_MONTH));
        }
        break;
      case YEAR :
        timeFrom.set(Calendar.DAY_OF_YEAR, 1);
        timeTo.set(Calendar.DAY_OF_YEAR, timeTo.getActualMaximum(Calendar.DAY_OF_YEAR));
        if (offset > 0) {
          timeFrom.add(Calendar.DAY_OF_YEAR, -1 * offset);
          timeTo.add(Calendar.DAY_OF_YEAR, -1 * offset);
          timeTo.set(Calendar.DAY_OF_YEAR, timeTo.getActualMaximum(Calendar.DAY_OF_YEAR));
        }
        break;
    }

    final Date tf = timeFrom.getTime();
    tf.setTime(tf.getTime() + timeZoneOffset * Constants.MILIS_IN_MINUTE);
    final Date tt = timeTo.getTime();
    tt.setTime(tt.getTime() + timeZoneOffset * Constants.MILIS_IN_MINUTE);

    return listOfEntityToDTO(getFoodDao().executeQuery("s.period", getUser().getUserId(), tf, tt));
  }
}
